import math
import matplotlib
import numpy as np
import geatpy as ea
import pandas as pd
from matplotlib import pyplot as plt

from pqi import PQI

matplotlib.rc('font', family='SimHei')
plt.rcParams['axes.unicode_minus'] = False

ROADLEN = 51.544


# pqi = PQI(N=math.ceil(ROADLEN))


class MyProblem(ea.Problem):  # 继承Problem父类
    def __init__(self, qpi, RoadLength=ROADLEN, budget=5000):
        '''
        RoadLength: float 维护路段总长度 (单位:KM)
        budget: float 养护路段总预算 (单位: 万元)
        '''
        self.road_len = RoadLength
        self.budget = budget
        self.m = math.ceil(RoadLength)

        self.pqi = qpi

        # self.C = np.array([0, 18.7, 53, 82, 80, 168, 280, 520]).reshape(-1, 1)  # 养护措施j的单价
        # self.E = np.array([0, 0.23, 0.8, 0.92, 1.44, 1.54, 1.60, 1.64]).reshape(-1, 1)  # 养护措施j的碳排放

        # ###决策变量: 交通量(整数), 路面破损状况PDCI(实数), 车辙深度RD(整数), 国际平整度指数IRI(实数), 横向力系数SFC(整数), 维护路段idx
        # 决策变量: 交通量(整数), 养护类别(整数)
        name = 'MD'
        T = 5
        M = 3  # 初始化M（目标维数）
        Dim = self.m  # 初始化Dim（决策变量维数x1,x2,x3）
        maxormins = [-1, 1, 1]  # 初始化maxormins (目标最小最大化标记列表, 1: 最小化目标, -1: 最大化目标)
        varTypes = [1] * self.m  # 初始化varTypes (决策变量类型, 0: 实数, 1: 整数)
        lb = [0] * self.m  # + [0] * self.m  # 决策变量下界
        ub = [7] * self.m  # + [self.m-1] * self.m  # 决策变量上界
        lbin = [1] * Dim  # 决策变量下边界（0表示不包含该变量的下边界，1表示包含）
        ubin = [1] * Dim  # 决策变量上边界（0表示不包含该变量的上边界，1表示包含）
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self,
                            name,
                            M,
                            maxormins,
                            Dim,
                            varTypes,
                            lb,
                            ub,
                            lbin,
                            ubin)

    # @ea.Problem.single
    def evalVars(self, Vars):  # 目标函数
        # b = Vars[:, [0]]  # 交通量
        js = Vars[:, :]  # 维护类别
        # j2 = Vars[:, [2]]  # 维护类别
        # j3 = Vars[:, [3]]  # 维护类别
        # j4 = Vars[:, [4]]  # 维护类别
        # j5 = Vars[:, [5]]  # 维护类别
        # j = np.stack([j1, j2, j3, j4, j5])

        f1 = self.pqi.F1(js)
        f2 = self.pqi.F2(js)
        f3 = self.pqi.F3(js)
        # print(f1)
        # print(f2)
        # print(f3)
        f = np.hstack([f1, f2, f3])
        # 约束条件
        CV = np.hstack([
            f2 - self.budget
        ])
        return f, CV


def stand_score(x, xmax, xmin):
    # print('X: {}'.format(x))
    z = (xmax - x) * 10 / (xmax - xmin)
    return z


def result_format_output(ObjV):
    # ObjV = result['ObjV']
    pqi = ObjV[:, [0]]
    cost = ObjV[:, [1]]
    carbon = ObjV[:, [2]]
    pqi_max = np.max(pqi)
    pqi_min = np.min(pqi)
    cost_max = np.max(cost)
    cost_min = np.min(cost)
    carbon_max = np.max(carbon)
    carbon_min = np.min(carbon)
    print('PQI max: {}, min: {}'.format(pqi_max, pqi_min))
    print('const max: {}, min: {}'.format(cost_max, cost_min))
    print('carbon max: {}, min: {}'.format(carbon_max, carbon_min))
    pqi_score = stand_score(pqi, pqi_max, pqi_min)
    cost_score = stand_score(cost, cost_max, cost_min)
    carbon_score = stand_score(carbon, carbon_max, carbon_min)
    scores = 0.4 * pqi_score + 0.4 * cost_score + 0.2 * carbon_score
    print(scores)
    print(type(scores))

    # df = pd.DataFrame(columns=['养护费用', '标准化分值', '碳排放量', '标准化分值', '路面质量指数', '标准化分值', '总分'])
    data = np.hstack((cost, cost_score, carbon, carbon_score, pqi, pqi_score, scores))
    df = pd.DataFrame(data, columns=['养护费用', '标准化分值', '碳排放量', '标准化分值', '路面质量指数', '标准化分值',
                                     '总分'])
    print(df)
    df.to_csv('outputs/养护方案1.csv')
    print('Max score index: {}'.format(np.argmax(scores)))
    return np.argmax(scores)


def scatter_3d(x, y, z):
    # 散点图
    fig = plt.figure()
    ax = fig.add_subplot(projection='3d')

    # s：marker标记的大小
    # c: 颜色  可为单个，可为序列
    # depthshade: 是否为散点标记着色以呈现深度外观。对 scatter() 的每次调用都将独立执行其深度着色。
    # marker：样式
    print('X: {}, Y: {}, Z: {}'.format(x, y, z))
    ax.scatter(x, y, z)
    xmax = np.max(x) + 0.001 * np.mean(x)
    xmin = np.min(x) - 0.001 * np.mean(x)
    ymax = np.max(y) + 0.001 * np.mean(y)
    ymin = np.min(y) - 0.001 * np.mean(y)
    zmax = np.max(z) + 0.001 * np.mean(z)
    zmin = np.min(z) - 0.001 * np.mean(z)
    print('zmax: {}, zmin: {}'.format(zmax, zmin))
    ax.set_xlim3d(xmin, xmax)
    ax.set_ylim3d(ymin, ymax)
    ax.set_zlim3d(zmin, zmax)

    ax.set_xlabel('养护费用(万元)')
    ax.set_ylabel('碳排放量(吨)')
    ax.set_zlabel('路面质量指数PQI')
    plt.savefig('outputs/养护决策结果.png', dpi=300)
    plt.show()


def calc_results(var):
    # var = np.array(
    #     [[0, 5, 5, 7, 3, 3, 3, 3, 7, 3, 3, 5, 7, 7, 7, 5, 7, 3, 7, 5, 3, 5, 5, 7, 3, 7, 6, 3, 7, 7, 7, 7, 4, 6, 7, 3, 7,
    #       5, 7, 7, 7, 7, 3, 5, 6, 5, 7, 6, 7, 7, 5, 5, 3]]).reshape(-1, 1)
    var = var.reshape(-1, 1)
    # b = var[0]
    js = var[:]
    rds = np.array(pd.read_csv('outputs/养护指标.csv', usecols=['rd']))
    f2 = pqi.F2_result(rds, js)
    f3 = pqi.F3_result(rds, js)
    print('F2 result: {}'.format(f2))
    print('F3 result: {}'.format(f3))


if __name__ == '__main__':
    pqi = PQI(N=math.ceil(ROADLEN))

    # 实例化问题对象
    problem = MyProblem(pqi)

    # 构造算法
    model = ea.moea_NSGA2_templet(
        problem,
        ea.Population(Encoding='RI', NIND=100),
        MAXGEN=300,  # 最大进化代数
        logTras=1  # 表示每隔多少代记录一次日志, 0表示不记录
    )
    # algorithm.mutOper.Pm = 0.6  # 修改变异算子的变异概率
    # algorithm.recOper.XOVR = 0.8  # 修改交叉算子的交叉概率

    # 求解
    res = ea.optimize(model,
                      verbose=True,
                      drawing=1,
                      outputMsg=True,
                      drawLog=True,
                      saveFlag=True,
                      dirName='result')
    print('result: {}'.format(res))
    if res['success']:
        ObjV = res['ObjV']
        print(ObjV)
        x = ObjV[:, 1]
        y = ObjV[:, 2]
        z = ObjV[:, 0]
        print(x, y, z)
        scatter_3d(x, y, z)
        max_idx = result_format_output(ObjV)

        Vars = res['Vars']
        # print(Vars[max_idx])
        calc_results(Vars[max_idx])
        # calc_result(self, j, maintainDept, roadNo, startingStation, year, duration)
        csvfile = pqi.calc_result(Vars[max_idx], maintainDept='10000000040014', roadNo='G1', startingStation='102.000', year=2023, duration=5)
        print(csvfile)
        # res = ea.optimize(algorithm, verbose=False, drawing=1, outputMsg=True, drawLog=False, saveFlag=False, dirName='result')


# {'maintainDept': '10000000040014', 'roadNo': 'G1', 'strategyParamList': [{'id': '1', 'maintainDept':
# '10000000040014', 'maintainDeptName': None, 'roadNo': 'G1', 'roadName': None, 'startingStation': '102.000',
# 'roadLen': 199.682, 'duration': 3, 'budgetFee': 5800.22, 'trafficLevel': 3, 'inTime': '2022-12-05 17:34:57',
# 'upTime': '2022-12-05 17:34:57', 'endingStation': None, 'lineTotal': 6, 'sectionRoad': None}, {'id': '2',
# 'maintainDept': '10000000040014', 'maintainDeptName': None, 'roadNo': 'G1', 'roadName': None, 'startingStation':
# '41.904', 'roadLen': 21.303, 'duration': 7, 'budgetFee': 3100.0, 'trafficLevel': 3, 'inTime': '2022-12-05
# 17:34:57', 'upTime': '2022-12-09 09:49:27', 'endingStation': None, 'lineTotal': 6, 'sectionRoad': None}]}
